﻿#nullable enable

using System;
using System.Collections.Generic;
using System.Threading.Tasks;
using Microsoft.Extensions.Configuration;
using PHMEE.CommonUtils;
using PHMEE.Localization;
using Volo.Abp;
using Volo.Abp.Application.Dtos;

namespace PHMEE.EnergyEfficiency;

public class BunkerReportAppService : PHMEEAppService, IBunkerReportAppService
{
    private readonly IPHMEETimeProvider _phmeeTimeProvider;
    private readonly IBunkerReportRepository _bunkerReportRepository;
    private readonly ITimeSeriesDataLogRepository _timeSeriesDataLogRepository;
    
    // 是否是船端
    private readonly bool _isBoard;

    public BunkerReportAppService(IPHMEETimeProvider phmeeTimeProvider,
        IBunkerReportRepository bunkerReportRepository,
        ITimeSeriesDataLogRepository timeSeriesDataLogRepository,
        IConfiguration configuration)
    {
        LocalizationResource = typeof(EnergyEfficiencyResource);
        
        _phmeeTimeProvider = phmeeTimeProvider;
        _bunkerReportRepository = bunkerReportRepository;
        _timeSeriesDataLogRepository = timeSeriesDataLogRepository;
        
        _isBoard = configuration.GetValue<bool>("IsBoard");
    }
    
    /// <inheritdoc />
    public async Task<CheckTsExistDto> CheckTsExistAsync(Guid vesselId, CreateUpdateBunkerReportDto input)
    {
        var bunkerDeliveryDateTime = DateTimeUtils.GetUtcDateTimeByDateOnlyTimeOnlyUpToMinute(input.BunkerDeliveryDate, input.BunkerDeliveryTime);
        
        // 检查同一时间戳是否已经存在 BunkerReport
        var existingBunkerReport = await _bunkerReportRepository.GetAsync(vesselId, bunkerDeliveryDateTime);
        
        return existingBunkerReport is not null 
            ? new CheckTsExistDto(true, L[PHMEEEnergyEfficiencyErrorCodes.ReportAlreadyExistsAtThisTs]) 
            : new CheckTsExistDto(false);
    }
    
    /// <inheritdoc />
    public async Task<BunkerReportDto?> GetDetailAsync(Guid vesselId, DateTime ts)
    {
        ts = ts.ToUtcKind();
        var bunkerReport = await _bunkerReportRepository.GetAsync(vesselId, ts);
        return bunkerReport is null ? null : MapToDto(bunkerReport);
    }

    /// <inheritdoc />
    public Task CreateAsync(Guid vesselId, CreateUpdateBunkerReportDto input)
    {
        return CreateAsync(vesselId, input, false, null);
    }

    /// <inheritdoc />
    public async Task UpdateAsync(Guid vesselId, DateTime ts, CreateUpdateBunkerReportDto input)
    {
        // 只允许修改一周内的加油量报告
        ts = ts.ToUtcKind();
        if (ts < _phmeeTimeProvider.GetUtcNow().AddDays(-7))
        {
            throw new UserFriendlyException(L[PHMEEEnergyEfficiencyErrorCodes.CannotModifyReportBeforeSevenDays]);
        }
        
        var originalBunkerReport = await _bunkerReportRepository.GetAsync(vesselId, ts);
        
        await CreateAsync(vesselId, input, true, originalBunkerReport);
    }
    
    private async Task CreateAsync(Guid vesselId, CreateUpdateBunkerReportDto input, bool isUpdate, BunkerReport? originalBunkerReport)
    {
        var bunkerDeliveryDateTime = DateTimeUtils.GetUtcDateTimeByDateOnlyTimeOnlyUpToMinute(input.BunkerDeliveryDate, input.BunkerDeliveryTime);
        
        // 暂不检查填写的油品是否在船舶配置的油品之内，因为加油量报告功能相对独立，不参与计算

        var utcNow = _phmeeTimeProvider.GetUtcNow();
        DateTime creationTime = utcNow;

        // 如果是更新操作，则需要把 creationTime 设置为原始数据的 creationTime
        if (isUpdate)
        {
            if (originalBunkerReport is not null)
            {
                creationTime = originalBunkerReport.CreationTime;
            }
        }

        var bunkerReport = new BunkerReport(
            ts: bunkerDeliveryDateTime,
            id: vesselId,
            bunkerDeliveryNoteNumber: input.BunkerDeliveryNoteNumber,
            bunkerDeliveryDateTime: bunkerDeliveryDateTime,
            fuelType: input.FuelType,
            mass: (float)Math.Round(input.Mass, 2, MidpointRounding.AwayFromZero),
            creationTime: creationTime,
            lastModifiedTime: utcNow);

        await _bunkerReportRepository.InsertAsync(bunkerReport, vesselId);
        
        // 如果是船端，则插入数据同步日志
        if (_isBoard)
        {
            // 插入 TimeSeriesDataLog 数据同步日志
            await _timeSeriesDataLogRepository.InsertAsync(new TimeSeriesDataLog(GuidGenerator.Create(), vesselId)
            {
                Ts = bunkerReport.Ts,
                SyncDataType = SyncDataTypeUtils.GetSyncDataType(typeof(BunkerReport)),
                OperationType = OperationType.Insert,
                OperationTime = DateTime.UtcNow
            });
        }
    }

    /// <inheritdoc />
    public async Task DeleteAsync(Guid vesselId, DateTime ts)
    {
        ts = ts.ToUtcKind();
        await _bunkerReportRepository.DeleteAsync(vesselId, ts);
        
        // 如果是船端，则插入数据同步日志
        if (_isBoard)
        {
            // 插入数据同步日志
            await _timeSeriesDataLogRepository.InsertAsync(new TimeSeriesDataLog(GuidGenerator.Create(), vesselId)
            {
                Ts = ts,
                SyncDataType = SyncDataTypeUtils.GetSyncDataType(typeof(BunkerReport)),
                OperationType = OperationType.Delete,
                OperationTime = DateTime.UtcNow
            });
        }
    }

    /// <inheritdoc />
    public async Task<PagedResultDto<BunkerReportDto>> GetListAsync(Guid vesselId,
        PagedAndSortedWithTimeRangeResultRequestDto input)
    {
        var (startDateTime, endDateTime) = QueryDateTimeUtils.ProcessForDateRangeQuery(input.StartTime, input.EndTime);
        var totalCount = await _bunkerReportRepository.CountAsync(vesselId, startDateTime, endDateTime);
        var bunkerReports = await _bunkerReportRepository.GetListDescAsync(vesselId, startDateTime, endDateTime,
            input.SkipCount, input.MaxResultCount);
        var resultDtos = MapToDtoList(bunkerReports);
        var result = new PagedResultDto<BunkerReportDto>(totalCount, resultDtos);
        return result;
    }
    
    private static List<BunkerReportDto> MapToDtoList(IReadOnlyList<BunkerReport> bunkerReports)
    {
        var dtos = new List<BunkerReportDto>();
        
        foreach (var bunkerReport in bunkerReports)
        {
            dtos.Add(MapToDto(bunkerReport));
        }

        return dtos;
    }

    private static BunkerReportDto MapToDto(BunkerReport bunkerReport)
    {
        var bunkerDeliveryDateTime = bunkerReport.BunkerDeliveryDateTime;
        
        var bunkerDeliveryDate = DateOnly.FromDateTime(bunkerDeliveryDateTime);
        var bunkerDeliveryTime = TimeOnly.FromDateTime(bunkerDeliveryDateTime);

        var ts = bunkerReport.Ts.ToUniversalTime();
        var creationTime = bunkerReport.CreationTime.ToUniversalTime();

        return new BunkerReportDto
        (ts, bunkerReport.Id, bunkerReport.BunkerDeliveryNoteNumber, bunkerDeliveryDate,
            bunkerDeliveryTime, bunkerReport.FuelType, bunkerReport.Mass, creationTime);
    }
}